/**
 * 抽象类
 *      抽象类做为其它派生类的基类使用。 
 *      它们一般不会直接被实例化。
 *      不同于接口，抽象类可以包含成员的实现细节。 
 *      abstract关键字是用于定义抽象类和在抽象类内部定义抽象方法。
 */
abstract class Animals {
    abstract makeSound(): void;
    move(): void {
        console.log('roaming the earch...');
    }
}

/**
 * 抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。 
 * 抽象方法的语法与接口方法相似。 
 * 两者都是定义方法签名但不包含方法体。 
 * 然而，抽象方法必须包含 abstract关键字并且可以包含访问修饰符。
 */
abstract class Department {

    constructor(public name: string) {
    }

    printName(): void {
        console.log('Department name: ' + this.name);
    }

    abstract printMeeting(): void; // 必须在派生类中实现
}

class AccountingDepartment extends Department {

    constructor() {
        super('Accounting and Auditing'); // 在派生类的构造函数中必须调用 super()
    }

    printMeeting(): void {
        console.log('The Accounting Department meets each Monday at 10am.');
    }

    generateReports(): void {
        console.log('Generating accounting reports...');
    }
}

let department: Department; // 允许创建一个对抽象类型的引用
//department = new Department(); // 错误: 不能创建一个抽象类的实例
department = new AccountingDepartment(); // 允许对一个抽象子类进行实例化和赋值
department.printName();
department.printMeeting();
//department.generateReports(); // 错误: 方法在声明的抽象类中不存在

/*
    构造函数
        当你在TypeScript里声明了一个类的时候，实际上同时声明了很多东西。 
        首先就是类的 实例的类型。
*/

class Greeters2 {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}

let greeters2: Greeters2;
greeters2 = new Greeters2("world");
console.log(greeters2.greet());